import re

import allure
from jsonpath import jsonpath

from src.log import log
from src.common import an2cn, is_digit, read_extract
from src.db import MysqlUtil
from src.config_util import Config
from src.yaml_util import Yaml


class Assert(object):
    def __init__(self, response, validate_obj):
        self.validate_obj = validate_obj
        self.response = response
        self.env = Config().get_conf("env")
        self.env_info = Config("conf/env.yml").get_conf(self.env)
        self.mapping = {
            "eq": "assert_equal",
            "not_eq": "assert_not_equal",
            "lt": "assert_less_than",
            "le": "assert_less_than_or_equals",
            "gt": "assert_greater_than",
            "ge": "assert_greater_than_or_equals",
            "len_eq": "assert_length_equals",
            "len_lt": "assert_length_less_than",
            "len_le": "assert_length_less_than_or_equals",
            "len_gt": "assert_length_greater_than",
            "len_ge": "assert_length_greater_than_or_equals",
            "contains": "assert_contains",
            "contained_by": "assert_contained_by",
            "startswith": "assert_startswith",
            "endswith": "assert_endswith"
        }

    def valid(self):
        try:
            for valid_item in self.validate_obj:
                key = list(valid_item.keys())[0]
                valid_list = [i.strip() for i in valid_item.get(key).split(",")]
                expression = valid_list[0]
                expect_value = valid_list[1]
                func = self.mapping.get(key)
                try:
                    if func:
                        getattr(self, func)(expression, expect_value)
                        with allure.step(
                                f"断言({an2cn(self.validate_obj.index(valid_item) + 1)}): 断言方式: {self.mapping.get(key)}, 断言内容: {valid_list}, 断言结果: 断言通过"):
                            log.info(
                                f"断言({an2cn(self.validate_obj.index(valid_item) + 1)}): 断言方式: {self.mapping.get(key)}, 断言内容: {valid_list}, 断言结果: 断言通过")
                    else:
                        raise Exception(f"不支持的断言方式: {key}")
                except Exception as e:
                    with allure.step(
                            f"断言({an2cn(self.validate_obj.index(valid_item) + 1)}): 断言方式: {self.mapping.get(key)}, 断言内容: {valid_list}, 断言结果: 断言失败。 失败原因: {e}"):
                        log.error(
                            f"断言({an2cn(self.validate_obj.index(valid_item) + 1)}): 断言方式: {self.mapping.get(key)}, 断言内容: {valid_list}, 断言结果: 断言失败。 失败原因: {e}")
                    raise Exception(e)
        except Exception as e:
            raise Exception(e)
        else:
            return True

    def __parse_expression(self, expression):
        # 正则表达式提取
        if expression.startswith("${/") and expression.endswith("/}"):
            return self.exec_reg(expression[3: -2].strip())
        # 模版语法提取值
        elif expression.startswith("${") and expression.endswith("}"):
            # 判断是否为sql
            if "select" in expression.lower() and "from" in expression.lower():
                return self.exec_sql(expression[2:-1].strip())
            # 判断是否为可执行函数
            elif "(" in expression:
                return eval(expression[2: -1].strip())
            else:
                raise Exception(f"不支持的变量提取方式{expression}")
        # jsonpath提取值
        elif expression.startswith("$."):
            return self.exec_jsonpath(expression)
        # response对象取值
        return self.exec_response(expression)

    def __parse_expect_value(self, expect_value):
        # 判断是否为sql
        if expect_value.startswith("${") and expect_value.endswith("}"):
            # 判断是否为sql
            if "select" in expect_value.lower() and "from" in expect_value.lower():
                return self.exec_sql(expect_value[2:-1].strip())
            # 判断是否为可执行函数
            elif "(" in expect_value:
                return eval(expect_value[2: -1].strip())
            # 认为是字符串
            else:
                # 判断字符串是否为extract中变量
                result = read_extract(expect_value[2: -1].strip())
                if result is not None:
                    return result
                else:
                    # 返回原字符串
                    return expect_value[2: -1].strip()
        # jsonpath提取值
        elif expect_value.startswith("$."):
            try:
                # 优先从extract.yml文件中读取变量
                result = self.extract_extract_info(expect_value)
            except Exception:
                # extraction.yml中没有，则从conf.yml文件中的测试环境信息读取变量
                result = self.extract_conf_info(expect_value)
            return result
        return expect_value

    def exec_jsonpath(self, expression):
        try:
            return jsonpath(self.response.json(), expression)[0]
        except Exception:
            raise Exception(f"jsonpath表达式错误或非预期响应内容。表达式: {expression};响应内容: {self.response.json()}")

    def exec_sql(self, sql):
        try:
            mysql = MysqlUtil(**self.env_info.get("mysql"))
            return list(mysql.query_sql(sql).values())[0]
        except Exception:
            raise Exception(f"sql语句错误或执行异常。sql语句: {sql}")

    def exec_reg(self, reg_expression):
        try:
            result = re.search(reg_expression, self.response.text, flags=re.S).group()
            if is_digit(result):
                return eval(result)
            else:
                return result
        except AttributeError:
            raise Exception(f"正则表达式或非预期响应内容。表达式: {reg_expression}; 响应内容: {self.response.text}")

    def exec_response(self, attr):
        if attr in ["status_code", "url", "ok", "encoding"]:
            return getattr(self.response, attr)
        else:
            raise Exception(f"不支持提取的Response对象属性{attr}")

    def extract_conf_info(self, expression):
        """通过jsonpath提取conf.yml中测试环境信息"""
        return jsonpath(self.env_info, expression)[0]

    def extract_extract_info(self, expression):
        """通过jsonpath提取extract.yml中测试环境信息"""
        return jsonpath(Yaml(f"extraction/{self.env}_extract.yml").read_yml(), expression)[0]

    def assert_equal(self, expression, check_value):
        expect_value = self.__parse_expression(expression)
        check_value = self.__parse_expect_value(check_value)
        if isinstance(expect_value, (int, float)) and isinstance(check_value, str):
            check_value = eval(check_value)
        assert check_value == expect_value, f'{expect_value} == {check_value}'

    def assert_not_equal(self, expression, check_value):
        expect_value = self.__parse_expression(expression)
        check_value = self.__parse_expect_value(check_value)
        if isinstance(expect_value, (int, float)) and isinstance(check_value, str):
            check_value = eval(check_value)
        assert check_value != expect_value, f'{expect_value} i= {check_value}'

    def assert_less_than(self, expression, check_value):
        expect_value, check_value = self.__to_digit(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert expect_value < check_value, f'{expect_value} < {check_value}'

    def assert_less_than_or_equals(self, expression, check_value):
        expect_value, check_value = self.__to_digit(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert expect_value <= check_value, f'{expect_value} <= {check_value}'

    def assert_greater_than(self, expression, check_value):
        expect_value, check_value = self.__to_digit(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert expect_value > check_value, f'{expect_value} > {check_value}'

    def assert_greater_than_or_equals(self, expression, check_value):
        expect_value, check_value = self.__to_digit(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert expect_value >= check_value, f'{expect_value} >= {check_value}'

    def assert_contains(self, expression, check_value):
        expect_value, check_value = self.__to_str(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert check_value in expect_value, f'{check_value} in {expect_value}'

    def assert_contained_by(self, expression, check_value):
        expect_value, check_value = self.__to_str(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert expect_value in check_value, f'{expect_value} in {check_value}'

    def assert_startswith(self, expression, check_value):
        expect_value, check_value = self.__to_str(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert expect_value.startswith(check_value), f'{expect_value} 以 {check_value} 开头'

    def assert_endswith(self, expression, check_value):
        expect_value, check_value = self.__to_str(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert expect_value.endswith(check_value), f'{expect_value} 以 {check_value} 结尾'

    def assert_length_equals(self, expression, check_value):
        expect_value, check_value = self.__to_str(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert len(expect_value) == int(check_value), f'{len(expect_value)} == {check_value}'

    def assert_length_less_than(self, expression, check_value):
        expect_value, check_value = self.__to_str(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert len(expect_value) < int(check_value), f'{len(expect_value)} < {check_value}'

    def assert_length_less_than_or_equals(self, expression, check_value):
        expect_value, check_value = self.__to_str(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert len(expect_value) <= int(check_value), f'{len(expect_value)} <= {check_value}'

    def assert_length_greater_than(self, expression, check_value):
        expect_value, check_value = self.__to_str(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert len(expect_value) > int(check_value), f'{len(expect_value)} > {check_value}'

    def assert_length_greater_than_or_equals(self, expression, check_value):
        expect_value, check_value = self.__to_str(
            self.__parse_expression(expression),
            self.__parse_expect_value(check_value)
        )
        assert len(expect_value) >= int(check_value), f'{len(expect_value)} >= {check_value}'

    def __to_str(self, expect_value, check_value):
        if isinstance(expect_value, (int, float)):
            expect_value = str(expect_value)
        if isinstance(check_value, (int, float)):
            check_value = str(check_value)
        return expect_value, check_value

    def __to_digit(self, expect_value, check_value):
        if isinstance(expect_value, str) and is_digit(expect_value):
            expect_value = eval(expect_value)
        if isinstance(check_value, str) and is_digit(check_value):
            check_value = eval(check_value)
        return expect_value, check_value
